package com.zhan.utils.lock;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.zhan.utils.jwt.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author 白尚兵
 * @date 2019/9/18 15:00
 **/
@Aspect
@Component
@Slf4j
@ConditionalOnClass(RedissonClient.class)
public class RedisLockAspect {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private JwtUtils jwtUtils;

    @Pointcut("@annotation(RedisLock)")
    public void lockAspect() {

    }

    @Around("lockAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = getKey(joinPoint);
        log.info("====>lockKey is " + key);
        RLock lock = redissonClient.getLock(key);
        RedisLock annotation = getAnnotation(joinPoint);
        try {
            lock.lock(annotation.waitTime(), TimeUnit.SECONDS);
            return joinPoint.proceed();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    private RedisLock getAnnotation(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return method.getAnnotation(RedisLock.class);
    }

    private String getKey(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RedisLock annotation = method.getAnnotation(RedisLock.class);
        LockType lockType = annotation.type();
        switch (lockType) {
            case METHOD_PARAMS:
                return getMethodName(joinPoint) + getParams(joinPoint);
            case KEY:
                String key = annotation.key();
                if (StrUtil.isNotBlank(key)) {
                    return key;
                }
            case TOKEN_METHOD:
                Long userId = jwtUtils.getUserId();
                return userId + getMethodName(joinPoint);
            default:
                return getMethodName(joinPoint);
        }
    }

    private String getMethodName(ProceedingJoinPoint joinPoint) {
        return joinPoint.toLongString();
    }

    private String getParams(ProceedingJoinPoint joinPoint) {
        return JSONObject.toJSONString(joinPoint.getArgs());
    }
}
